home *** CD-ROM | disk | FTP | other *** search
- /*
- Name: Diff.c
- Processor: VAX | MS-DOS
- Class: C Program
- Creation Date: 1/8/87
- Revision:
- Author: D. Krantz
-
- Description: File compare and change-bar for text files.
- */
-
- /*
- Source: Dr. Dobb's Journal of Software Tools
- Issue: #130 August 1987
- Article: UTILITIES: What's the DIFF? Page 30.
- PD Validation: Page 39, Column 1, Paragraph 6
- "I am releasing DIFF into the public domain..."
-
- Amiga Port: Jeffrey Bailey
- Revision Date: 10/5/87
-
- Major Changes: All internal tracing and debugging routines removed.
- Memory allocation tracking routines added.
- CTRL-C trapping added.
- Minor bugs fixed.
- Linked list routines partially re-written to allow better
- usage of memory with a tradeoff in performance.
- (storage for lines is dynamically allocated to the
- exact size needed)
- Added /SOURCE_CODE option.
- Added /TRIM & /NOTRIM options.
- Changed /UP_CASE to /NOEXACT for better VMS compatibility.
- Added register declarations for optimization purposes.
-
- Known Bugs and Problems:
-
- Diff looks for options to start with a forward slash '/'.
- If a file spec starts with a forward slash, Diff will
- assume it to be an option and will probably generate an
- error message. Two possible solutions:
-
- o Always specify file names in such a way that no
- leading slashes occur.
-
- o Change OPT_FLAG to another character and re-compile,
- losing VMS command line "look and feel".
-
- Options may be abbreviated. For example /SOURCE_CODE may
- be abbreviated as /SOURCE. However, if you do not specify
- enough characters, the option may be ambiguous. If this
- happens, Diff will not issue a warning; the parser will
- accept this situation on a first come, first served basis.
- This was a problem from the original version. I just didn't
- think it was worth the effort to fix it.
-
- Polling for CTRL-C adds overhead. I just didn't know of a
- better way to do it. Also, instead of the standard "^C",
- this program prints " Interrupt " in reverse video to
- maintain the VMS "look and feel". Feel free to change it.
-
- The memory allocation tracking routines add even more
- overhead. However, they are necessary because the author
- of this program was sloppy about resource tracking (VMS
- does it for you, and with MS-DOS it's not a concern).
- I couldn't use the system routines AllocRemember() and
- FreeRemember() because this program frees individual
- chunks of memory at different points in time.
- FreeRemember() is an all or nothing process -- unsuitable
- for this program. (If I'm incorrect about this, please
- feel free to change the source. This is my first major
- project on the Amiga and I'm not totally familiar with
- the OS yet.)
- */
- #include <stdio.h>
- #include <ctype.h>
-
- /*
- All of these #include's are probably not necessary, but the C compiler
- always seems to throw a fit if they aren't in the right order or something.
- */
- #include "exec/types.h"
- #include "exec/nodes.h"
- #include "exec/lists.h"
- #include "exec/ports.h"
- #include "exec/interrupts.h"
- #include "exec/io.h"
- #include "exec/memory.h"
- #include "libraries/dos.h"
- #include "libraries/dosextens.h"
-
- /*
- Declaration for my memory routine.
- */
- char *TrackMalloc();
-
- /*
- Specifies the flag character.
- */
- #define OPT_FLAG '/'
-
- #define MAXLINE 2048
-
- #define FORMFEED '\014'
-
- struct LINE {
- unsigned long linenum;
- unsigned long pagenum;
- struct LINE *link;
- char *text;
- char *dup;
- };
-
- typedef struct LINE *line_ptr;
-
- typedef char *char_ptr;
-
- typedef FILE *FILE_PTR;
-
- struct LINE root [3];
-
- FILE_PTR msg;
-
- unsigned long line_count [3] = {1, 1, 1};
- unsigned long page_count [3] = {1, 1, 1};
-
- int command_errors = 0;
- char xx1 [132], xx2 [132];
- int files = 0;
- char_ptr infile_name [3] = {NULL, xx1, xx2};
- char outfile_name [132];
- FILE_PTR infile [3];
- FILE *outfile;
- static line_ptr at [3] = {NULL, &(root [1]), &(root [2])};
-
- int debug = 0;
- int trace_enabled = 0;
- int bar_col = 0;
- unsigned long top_skip = 0;
- unsigned long bot_skip = 0;
- unsigned long page_len = 66;
- int up_case = 0;
- int re_sync = 5;
- int output = 0;
- int blanks = 0;
- int lookahead = 200;
- int skip1 = 0;
- int skip2 = 0;
- int trim_lines = 0;
-
- main(argc, argv)
- int argc;
- char *argv [];
- {
- int i;
-
- /* Initialize our file pointers so cleanup() only closes open files. */
- i = 0;
- while(i < 3)
- {
- infile [i] = NULL;
- i++;
- }
- outfile = NULL;
- if(argc == 1) help();
- msg = stdout;
- for(i = 1; i < argc; i++) strip_opt(argv [i]);
- if(files < 2)
- {
- printf("DIFF: Error: Must specify two files.\n");
- cleanup();
- }
- open_files();
- if(command_errors)
- cleanup();
- page_skip();
- diff();
- cleanup();
- }
-
- dont_look(line)
- register line_ptr line;
- {
- register char *temp;
-
-
- check_ctrl_c();
- if(line == NULL) return(0);
- if(line->linenum <= top_skip) return(1);
- if(line->linenum > page_len - bot_skip) return(1);
- temp = line->text;
- if(!blanks)
- {
- while(*temp != 0)
- {
- switch(*temp)
- {
- case ' ':
- case '\t':
- case '\n':
- break;
- default :
- return(0);
- break;
- }
- temp++;
- }
- return(1);
- }
- return(0);
- }
-
- equal(a, b)
- line_ptr a, b;
- {
- check_ctrl_c();
- if( (a == NULL) || (b == NULL) ) return(0);
- if(up_case) return(!strcmp(a->dup, b->dup));
- else return(!strcmp(a->text, b->text));
- }
-
- position(f, where)
- int f;
- line_ptr where;
- {
- check_ctrl_c();
- at [f] = &root [f];
- while(at [f]->link != where)
- at[f] = at [f]->link;
- return(0);
- }
-
- char *index(str, c)
- register char *str, c;
- {
- check_ctrl_c();
- while(*str != NULL)
- {
- if(*str == c) return(str);
- str++;
- }
- return(NULL);
- }
-
- line_ptr next_line(f)
- int f;
- {
- line_ptr place_hold;
- char temp [MAXLINE + 1];
-
- check_ctrl_c();
- if( at [f]->link != NULL)
- {
- at [f] = at [f]->link;
- return(at [f]);
- }
- else
- {
- at [f]->link = (line_ptr) TrackMalloc(sizeof(struct LINE));
- if(at [f]->link == NULL)
- {
- printf("DIFF: Out of Memory.\n");
- cleanup();
- }
- place_hold = at [f];
- at [f] = at [f]->link;
- at [f]->link = NULL;
- at [f]->text = NULL;
- at [f]->dup = NULL;
- if(fgets(temp, MAXLINE, infile [f]) == NULL)
- {
- TrackFree(at [f]);
- at [f] = place_hold;
- at [f]->link = NULL;
- return(NULL);
- }
- if(trim_lines)
- {
- trim(temp);
- strcat(temp, "\n");
- }
- at [f]->text = TrackMalloc(strlen(temp) + 1);
- if(at [f]->text == NULL)
- {
- printf("DIFF: Out of Memory.\n");
- cleanup();
- }
- strcpy(at [f]->text, temp);
- if( (index( at [f]->text, FORMFEED) != NULL) ||
- (line_count [f] > page_len))
- {
- page_count [f]++;
- line_count [f] = 1;
- }
- at [f]->linenum = line_count [f]++;
- at [f]->pagenum = page_count [f];
- if(up_case)
- {
- at [f]->dup = TrackMalloc(strlen(at [f]->text) + 1);
- if(at [f]->dup == NULL)
- {
- printf("DIFF: Out of Memory.\n");
- cleanup();
- }
- strcpy(at [f]->dup, at [f]->text);
- upper(at [f]->dup);
- }
- return(at [f]);
- }
- }
-
- discard(f, to)
- int f;
- line_ptr to;
- {
- register line_ptr temp;
-
- check_ctrl_c();
- while(1)
- {
- if(root [f].link == NULL) break;
- temp = root [f].link;
- root [f].link = root [f].link->link;
- TrackFree(temp->text);
- if(temp->dup != NULL) TrackFree(temp->dup);
- TrackFree(temp);
- if(temp == to) break;
- }
- at [f] = &root [f];
- return(0);
- }
-
- vfputs(str, file)
- char *str;
- FILE *file;
- {
- check_ctrl_c();
- fputs(str, file);
- return(0);
- }
-
- put(line)
- line_ptr line;
- {
- register line_ptr temp;
-
- if(output)
- for(temp = root [1].link; ;)
- {
- check_ctrl_c();
- if(temp == NULL) return(0);
- vfputs(temp->text, outfile);
- if(temp == line) return(0);
- temp = temp->link;
- }
- return(0);
- }
-
- char *change_bar(str)
- register char *str;
- {
- register int i;
- char temp [MAXLINE + 1], *base;
-
- check_ctrl_c();
- base = temp;
- strcpy(temp, str);
- i = 0;
- if(bar_col != 0)
- {
- while(*base != '\n')
- {
- if((*base == '\r') && (*(base + 1) != '\n')) i = 0;
- base++;
- i++;
- }
- while(i++ < bar_col) *(base)++ = ' ';
- strcpy(base, "|\n");
- }
- else
- {
- if(temp [0] != ' ')
- {
- strcpy(&temp [1], str);
- }
- temp [0] = '|';
- }
- TrackFree(str);
- base = TrackMalloc(strlen(temp) + 1);
- if(base == NULL)
- {
- printf("DIFF: Out of Memory.\n");
- cleanup();
- }
- strcpy(base, temp);
- return(base);
- }
-
- added(line)
- line_ptr line;
- {
- register line_ptr temp;
-
- check_ctrl_c();
- for(temp = root [1].link; ;)
- {
- if(temp == NULL) return(0);
- if(!dont_look(temp))
- fprintf(msg, "+ Page %d : Line %d -> %s", temp->pagenum, temp->linenum,
- temp->text);
- if(output)
- if(dont_look(temp)) vfputs(temp->text, outfile);
- else vfputs((temp->text = change_bar(temp->text)), outfile);
- if(temp == line) return(0);
- temp = temp->link;
- }
- }
-
- deleted(line)
- line_ptr line;
- {
- register line_ptr temp;
-
- check_ctrl_c();
- for(temp = root [2].link; ; )
- {
- if(temp == NULL) return(0);
- if(!dont_look(temp))
- fprintf(msg, "- Page %d : Line %d -> %s", temp->pagenum, temp->linenum,
- temp->text);
- if(temp == line) return(0);
- temp = temp->link;
- }
- return(0);
- }
-
- resync(first, second)
- line_ptr first, second;
- {
- register line_ptr file1_start, file2_start;
- line_ptr last_bad1, last_bad2, t1, t2;
- int i, j, k, moved1, moved2;
-
- check_ctrl_c();
- moved1 = 0;
- file1_start = first;
-
- position(1, first);
- for(k = 0; k < lookahead; k++)
- {
- while(dont_look(file1_start = next_line(1)));
- if(file1_start == NULL) goto no_sy;
-
- moved2 = 0;
- file2_start = second;
-
- position(2, second);
- for(j = 0; j < lookahead; j++)
- {
- while(dont_look(file2_start = next_line(2)));
- if(file2_start == NULL) goto eof2;
-
- t1 = file1_start;
- t2 = file2_start;
- for(i = 0; (i < re_sync) && equal(t1, t2); i++)
- {
- while(dont_look(t1 = next_line(1)));
- while(dont_look(t2 = next_line(2)));
- if( (t1 == NULL) || (t2 == NULL)) break;
- }
- if(i == re_sync) goto synced;
-
- last_bad2 = file2_start;
- position(2, file2_start);
- while(dont_look(file2_start = next_line(2)));
- moved2++;
- }
- eof2:
- check_ctrl_c();
- last_bad1 = file1_start;
- position(1, file1_start);
- while(dont_look(file1_start = next_line(1)));
- moved1++;
- }
- printf("DIFF: Lost sync in file %s at page %d line %d.\n",
- infile_name [1], first->pagenum, first->linenum);
- fclose(outfile);
- outfile = NULL;
- cleanup();
- no_sy:
- check_ctrl_c();
- position(1, first);
- while( (first = next_line(1)) != NULL)
- {
- added(first);
- discard(1, first);
- }
- return(0);
- synced:
- check_ctrl_c();
- if(moved1)
- {
- added(last_bad1);
- discard(1, last_bad1);
- }
- position( 1, file1_start);
- if(moved2)
- {
- deleted(last_bad2);
- discard(2, last_bad2);
- }
- position(2, file2_start);
- fprintf(msg, "\n");
- return(0);
- }
-
- diff()
- {
- register line_ptr first, second;
-
- check_ctrl_c();
- while(1)
- {
- while(dont_look(first = next_line(1)));
- if(first == NULL)
- {
- put(first);
- return(0);
- }
- while(dont_look(second = next_line(2)));
- if(equal(first, second))
- {
- put(first);
- discard(1, first);
- discard(2, second);
- }
- else resync(first, second);
- if(second == NULL) return(0);
- }
- }
-
- page_skip()
- {
- register line_ptr first, second;
-
- check_ctrl_c();
- while(1)
- {
- first = next_line(1);
- if( (first == NULL) || (first->pagenum > skip1) ) break;
- put(first);
- discard(1, first);
- }
- if(first != NULL) position(1, first);
- while(1)
- {
- second = next_line(2);
- if( (second == NULL) || (second->pagenum > skip2) ) break;
- discard(2, second);
- }
- if(second != NULL) position(2, second);
- return(0);
- }
-
- help()
- {
- printf("\nDIFF");
- printf("\nText File Difference Detector");
- printf("\n");
- printf("\nFormat:");
- printf("\nDIFF [option {option} ...] newfile oldfile [barfile]");
- printf("\n");
- printf("\n newfile = latest revision of text file.");
- printf("\n oldfile = baseline to compare against.");
- printf("\n barfile = output file if change bars are desired.");
- printf("\n");
- printf("\nOptions:");
- printf("\n /BAR_COL=n Column of output file in which change bar");
- printf("\n will appear. Default = %d", bar_col);
- printf("\n /BLANKS Blank lines are to be considered significant.");
- printf("\n /NOBLANKS Blank lines are not significant. DEFAULT");
- printf("\n /BOT_SKIP=n Lines at bottom of page to skip for running");
- printf("\n footers and page numbers. Default = %d",
- bot_skip);
- printf("\n /EXACT Case is significant. DEFAULT");
- printf("\n /NOEXACT Upper/Lower case is not considered");
- printf("\n significant in the comparison.");
- printf("\n /LOOKAHEAD=n Maximum number of lines to look ahead in");
- printf("\n each file to resync after a difference.");
- printf("\n Default = %d", lookahead);
- printf("\n /OUTPUT=file File to which differences summary is written.");
- printf("\n Default is to stdout.");
- printf("\n /PAGE_LEN=n Lines per page. (embedded form feeds override");
- printf("\n this value) Default = %d", page_len);
- printf("\n /RE_SYNC=n Number of lines that must match before the");
- printf("\n files are considered synced. Default = %d",
- re_sync);
- printf("\n /SKIP1=n Pages in NEWFILE to skip before comparison");
- printf("\n starts. Also sets /SKIP2. Default = %d",
- skip1);
- printf("\n /SKIP2=n Pages in OLDFILE to skip before comparison.");
- printf("\n MUST BE AFTER /SKIP1 ! Default = %d", skip2);
- printf("\n /SOURCE_CODE Causes DIFF to stop counting lines for page");
- printf("\n breaks. Everything is considered to be");
- printf("\n on page 1. (embedded form feeds override)");
- printf("\n /TOP_SKIP=n Lines at top of page to skip for running");
- printf("\n headings and page numbers. Default = %d",
- top_skip);
- printf("\n /TRIM_LINES Trim trailing spaces, tabs, returns, and");
- printf("\n newlines from the lines before comparison.");
- printf("\n /NOTRIM_LINES Do not trim lines before comparison. DEFAULT");
- printf("\n\n");
- }
-
- open_files()
- {
- int i;
-
- check_ctrl_c();
- for(i = 1; i < 3; i++)
- if( (infile [i] = fopen(infile_name [i], "r")) == NULL)
- {
- printf("DIFF: Error: Can't open %s for READ!\n", infile_name [i]);
- command_errors++;
- }
- if(files > 2)
- if( (outfile = fopen(outfile_name, "w")) == NULL)
- {
- printf("DIFF: Error: Can't open %s for WRITE!\n", outfile_name);
- command_errors++;
- }
- return(0);
- }
-
- redirect(str)
- char *str;
- {
- char filename [132];
- register char *ptr, *dest;
-
- check_ctrl_c();
- dest = filename;
- if( (ptr = index(str, '=') + 1) == (char *) (NULL + 1) )
- {
- printf("DIFF: Error in option %s.\n", str);
- command_errors++;
- }
- while( (*(dest++) = *(ptr++)) != '\0');
- *dest = '\0';
- if( (msg = fopen(filename, "w")) == NULL)
- {
- printf("DIFF: Can't open %s for WRITE!\n", filename);
- command_errors++;
- }
- return(0);
- }
-
- strip_opt(str)
- char *str;
- {
- check_ctrl_c();
- upper(str);
- if( *str == OPT_FLAG)
- {
- if(match(str + 1, "BAR_COL"))
- bar_col = num(str);
- else if(match(str +1, "TOP_SKIP"))
- top_skip = num(str);
- else if(match(str +1, "BOT_SKIP"))
- bot_skip = num(str);
- else if(match(str + 1, "PAGE_LEN"))
- page_len = num(str);
- else if(match(str + 1, "NOEXACT"))
- up_case = 1;
- else if(match(str + 1, "EXACT"))
- up_case = 0;
- else if(match(str + 1, "RE_SYNC"))
- re_sync = num(str);
- else if(match(str + 1, "BLANKS"))
- blanks = 1;
- else if(match(str + 1, "NOBLANKS"))
- blanks = 0;
- else if(match(str + 1, "LOOKAHEAD"))
- lookahead = num(str);
- else if(match(str + 1, "SKIP1"))
- skip1 = skip2 = num(str);
- else if(match(str + 1, "SKIP2"))
- skip2 = num(str);
- else if(match(str + 1, "OUTPUT"))
- redirect(str);
- else if(match(str + 1, "SOURCE_CODE"))
- page_len = 2147483647L;
- else if(match(str + 1, "TRIM_LINES"))
- trim_lines = 1;
- else if(match(str + 1, "NOTRIM_LINES"))
- trim_lines = 0;
- else
- {
- printf("DIFF: Unrecognized Option: %s.\n", str);
- command_errors++;
- }
- }
- else
- {
- switch(files)
- {
- case 0:
- strcpy(infile_name [1], str);
- break;
- case 1:
- strcpy(infile_name [2], str);
- break;
- case 2:
- strcpy(outfile_name, str);
- output = 1;
- break;
- default :
- printf("DIFF: Error: Too many files specified at %s\n", str);
- command_errors++;
- break;
- }
- files++;
- }
- return(0);
- }
-
- upper(str)
- register char *str;
- {
- check_ctrl_c();
- while(*str != NULL)
- {
- *str = toupper(*str);
- str++;
- }
- }
-
- int match(str, pattern)
- register char *str, *pattern;
- {
- check_ctrl_c();
- while(1)
- {
- if(*str != *pattern) return(0);
- str++;
- pattern++;
- if(*pattern == '\0') return(1);
- if(*str == '\0') return(1);
- if(*str == '=') return(1);
- }
- }
-
- int num(str)
- char *str;
- {
- register char *temp;
-
- check_ctrl_c();
- temp = index(str, '=');
- if(temp == NULL) return(0);
- else return(atoi(temp + 1));
- }
-
- cleanup()
- {
- int i;
-
- /*
- Unnecessary, but a good habit to get into. Added when I was looking
- for a creeping memory loss.
- */
- i = 0;
- while(i < 3)
- {
- if(infile [i] != NULL) fclose(infile [i]);
- infile [i] = NULL;
- i++;
- }
- if(outfile != NULL) fclose(outfile);
- outfile = NULL;
- /* Free up any memory that's not been freed yet. Necessary. */
- TrackFreeAll();
- exit();
- }
-
- /*
- Node structure for our linked list that keeps track of memory allocations.
- This wouldn't be necessary if the original program wasn't so sloppy
- about it's allocations and deallocations. Note that this was added
- so the program wouldn't eat memory when run on an Amiga. (no resource
- tracking, ya know!) Also note that I couldn't use the native routines
- such as AllocRemember() on the Amiga, 'cause with them it's all or nothing,
- and this program selectively frees memory at odd times.
- */
-
- struct TrackNode {
- struct TrackNode *next;
- char *address;
- unsigned long size;
- };
-
- /*
- Base pointer for our linked list. Also an end-of-list pointer so we
- don't have to scan the entire list to find the end in order to add a
- new node. Saves lots of time!
- */
- static struct TrackNode *TrackBase = NULL;
- static struct TrackNode *TrackLEnd = NULL;
-
- char *TrackMalloc(msize)
- unsigned long msize;
- {
- register struct TrackNode *temp, *current;
- char *memory;
-
- temp = (struct TrackNode *) AllocMem(sizeof(struct TrackNode), MEMF_PUBLIC);
- if(temp == NULL) return(NULL);
- memory = (char *) AllocMem(msize, MEMF_PUBLIC);
- if(memory == NULL)
- {
- FreeMem(temp, sizeof(struct TrackNode));
- return(NULL);
- }
- if(TrackBase == NULL)
- {
- TrackBase = temp;
- TrackBase->next = NULL;
- TrackBase->address = memory;
- TrackBase->size = msize;
- TrackLEnd = TrackBase;
- }
- else
- {
- current = TrackLEnd;
- current->next = temp;
- current = current->next;
- current->next = NULL;
- current->address = memory;
- current->size = msize;
- TrackLEnd = current;
- }
- return(memory);
- }
-
- /*
- Unfortunately, in this case we must scan the list. This routine is
- probably relatively slow, although I optimized it as much as possible.
- */
- TrackFree(pointer)
- register char *pointer;
- {
- register struct TrackNode *current, *prev;
-
- current = TrackBase;
- if(current == NULL)
- {
- return(NULL);
- }
- prev = TrackBase;
- while(current->next != NULL && current->address != pointer)
- {
- prev = current;
- current = current->next;
- }
- if(current->address != pointer)
- {
- return(NULL);
- }
- if(current == TrackBase)
- {
- TrackBase = current->next;
- }
- else
- {
- prev->next = current->next;
- }
- FreeMem(current->address, current->size);
- FreeMem(current, sizeof(struct TrackNode));
- if(current == TrackLEnd) TrackLEnd = prev;
- return(1);
- }
-
- /*
- Free up all memory that's still allocated.
- */
-
- TrackFreeAll()
- {
- register struct TrackNode *current, *hold;
-
- current = TrackBase;
- while(current != NULL)
- {
- FreeMem(current->address, current->size);
- hold = current->next;
- FreeMem(current, sizeof(struct TrackNode));
- current = hold;
- }
- TrackBase = NULL;
- TrackLEnd = NULL;
- return(1);
- }
-
- trim(start)
- char *start;
- {
- int slen;
- register char *end;
-
- slen = strlen(start);
- if(slen == 0) return(0);
- end = start + slen - 1;
- while( (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')
- && end >= start) end--;
- end++;
- *end = '\0';
- }
-
- ctrl_c_pressed()
- {
- if(SetSignal(0l, 0l) & SIGBREAKF_CTRL_C)
- {
- return(1);
- }
- SetSignal(0l, SIGBREAKF_CTRL_C);
- return(NULL);
- }
-
- check_ctrl_c()
- {
- if(ctrl_c_pressed())
- {
- printf("\n\033[7m Interrupt \033[m\n\n");
- cleanup();
- }
- }
-